home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #49 (Oct 89) / DMP Source / src / PDriver.c < prev   
Text File  |  1989-01-02  |  14KB  |  479 lines

  1. /*
  2.  * This is the driver.  A description of the driver can be found
  3.  * in the Device Manager chapter of Inside Macintosh, and a
  4.  * description of THIS driver can be found in the Print Manager
  5.  * chapter.
  6.  */
  7. /*
  8.  * This file is part of the DMP-110 printer driver for the Macintosh
  9.  * series of computers.
  10.  */
  11. /*
  12.  * Earle R. Horton.
  13.  * Wednesday, November 30, 1988
  14.  * All rights reserved.
  15.  */
  16. #define DRIVER
  17. #include <Windows.h>
  18. #include <Events.h>
  19. #include <Dialogs.h>
  20. #include <Fonts.h>
  21. #include <Memory.h>
  22. #include <Resources.h>
  23. #include <ToolUtils.h>
  24. #include <Errors.h>
  25. #include <Desk.h>
  26. #include "dmp-110.h"
  27. #include "compat.h"
  28.  
  29. #define __SEG__ Main
  30.  
  31. #define lPrEvtAll       0x00FFFFFD
  32. #define lPrEvtTop       0x00FEFFFD
  33. #define dNeedGoodBye 4
  34.  
  35. int checkabort();
  36.  
  37. /* Useful constants */
  38. #define SERRESET    8
  39. #define SERSHAKE    10
  40.  
  41. #define XONCR       ((char)17)
  42. #define XOFFCR      ((char)19)
  43. #define RESFILEID   (-8192)
  44.  
  45. extern char AOutName[],BOutName[];
  46. extern short BaudRates[];
  47. extern char prlfstr[];
  48. extern char prinitstr[];
  49. extern char prtopstr[];
  50. extern char preopstr[];
  51. extern char prmargin[];
  52. extern char prhireslf[];
  53.  
  54. OSErr openprinterfile(),SPOpen();
  55. /*
  56.  * Driver close routine.
  57.  * Close serial port.  Dispose of our storage.
  58.  * Better not close the serial driver if we
  59.  * are dealing with 64k ROMs.
  60.  *
  61.  * Called by device manager under UniFinder when application
  62.  * heap is reinitialized.
  63.  */
  64. OSErr myPrClose(p,d)    /* Device Close Call */
  65.     PrParam *p;     /*  ==> parameter block  */
  66.     DCtlPtr d;      /*  ==> device control entry  */
  67. {
  68.     return(noErr);
  69. }
  70. #include <strings.h>
  71. /*
  72.  * Printer driver open routine.  Open our printer resource file, get
  73.  * any information we have stored there, allocate a non-relocatable
  74.  * block of storage, set up a serial port for use.  Check errors.
  75.  */
  76. OSErr myPrOpen(p,d)
  77.     PrParam *p;     /*  ==> parameter block  */
  78.     DCtlPtr d;      /*  ==> device control entry  */
  79. {
  80.     extern short DriverEntry;
  81.     return (noErr);
  82. }
  83. OSErr myPrPrime(p,d)    /* We don't do prime.  It's for drivers which do */
  84.                         /* read/write directly. */
  85.     PrParam *p;     /*  ==> parameter block  */
  86.     DCtlPtr d;      /*  ==> device control entry  */
  87. {
  88.     return(noErr);
  89. }
  90. OSErr myPrControl(p,d)  /* Control calls.  Many defined, few implemented. */
  91.     PrParam *p;     /*  ==> parameter block  */
  92.     DCtlPtr d;      /*  ==> device control entry  */
  93. {
  94.     Dstorage  storage;
  95.     char *buf = nil;
  96.     SysEnvRec       World;
  97.     OSErr error;
  98.     ProcPtr idle;
  99.  
  100.     SysEnvirons(1,&World);
  101.  
  102.     PrSetError(noErr);
  103.  
  104.     memset(&storage,0,sizeof(Dstorage));
  105.  
  106.     error = SPOpen(&storage,&World);
  107.     if(error != noErr) return noErr;
  108.  
  109.     storage.iopb.ioParam.ioResult = noErr;
  110.     if(setjmp(storage.abortbuf) != 0){
  111.         PBClose(&storage.iopb);
  112.         if(p->csCode == iPrEvtCtl){
  113.             DisposPtr(buf);
  114.         }
  115.         return noErr;
  116.     }
  117. /*
  118.  * Device Control Call.  p->csCode gives opcode, and we switch on it
  119.  * to perform low-level Printing calls
  120.  */
  121.     switch (p->csCode){
  122.  
  123.  
  124.         case iPrBitsCtl:      /* Send a bitmap to the printer. */
  125.             break;
  126.         case iPrEvtCtl:      /* Screen printing. (cmd-shift 4.)*/
  127.             if(*(short*)(&p->lParam1) == 1){        /* Top window. */
  128.                 buf = NewPtr(2000L);
  129.                 if(buf != nil){
  130.                     dumptop(&storage,buf);
  131.                     DisposPtr(buf);
  132.                 }
  133.             }
  134.             else if(*(short*)(&p->lParam1) == 2){   /* Screen. */
  135.                 buf = NewPtr(2000L);
  136.                 if(buf !=  nil){
  137.                     dumpscreen(&storage,buf);
  138.                     DisposPtr(buf);
  139.                     }
  140.             }
  141.             break;
  142.         default:
  143.             break;
  144.     }
  145.     PBClose(&storage.iopb);
  146.     return noErr;
  147. }
  148. /*
  149.  * Printer driver status call, used by the Font Manager to request
  150.  * a copy of the printer's font characterization table.  Ignored
  151.  * like the control call.
  152.  */
  153. OSErr myPrStatus(p,d)   /* Device Status Call */
  154.     PrParam *p;     /*  ==> parameter block  */
  155.     DCtlPtr d;      /*  ==> device control entry  */
  156. {
  157.     return (noErr);
  158. }
  159. /*
  160.  * Open the serial driver and configure it.  Quit if ioResult field
  161.  * of parameter block ever becomes other than noErr.
  162.  */
  163. OSErr SPOpen(s,World)
  164.  
  165. register DPstorage  s;
  166. SysEnvRec   *World;
  167. {
  168. register ParmBlkPtr pb;
  169. int     serconfig;
  170. Pfg     settings;
  171. Handle pfilename;
  172. short pfile;
  173.     pfilename = GetResource('STR ',0xE000);
  174.     if(pfilename ==  nil){
  175.         return ResError();
  176.     }
  177.     HLock(pfilename);
  178.     pfile = OpenResFile(*pfilename);
  179.     if(pfile == -1){
  180.         return ResError();
  181.     }
  182.     DisposHandle(pfilename);
  183.     settings = (Pfg)(GetResource('HEXA',RESFILEID));
  184.     if(settings == nil){
  185.         return ResError();
  186.     }
  187.     LoadResource(settings);
  188.     HNoPurge(settings);
  189.     pb = &s->iopb;
  190.     switch (pport){     /* get the correct port */
  191.         case 0:     /* modem port */
  192.             pb->ioParam.ioNamePtr = (StringPtr)AOutName;
  193.             break;
  194.         case 1:     /* printer port */
  195.         default:
  196.             if(IsMPPOpen()){
  197.                 (void)StopAlert(ATALKALERT,nil);
  198.                 return portInUse;
  199.             }
  200.             pb->ioParam.ioNamePtr = (StringPtr)BOutName;
  201.             break;
  202.     }
  203.     PBOpen(pb,FALSE);
  204.     if (pb->ioParam.ioResult != noErr){
  205.          return(pb->ioParam.ioResult);
  206.     }
  207.     pb->ioParam.ioNamePtr = nil;
  208. /*
  209.  * Set up the io parameter block for writing to the serial driver.
  210.  * a control call resets the baud rate
  211.  */
  212.     ((CntrlParam *)pb)->csCode = SERRESET;
  213.     serconfig = data8 + noParity + stop20;
  214.     serconfig += BaudRates[pbaud];
  215.     ((CntrlParam*)pb)->csParam[0] = serconfig;
  216.     PBControl(pb,FALSE);
  217.     if (pb->ioParam.ioResult != noErr) return(pb->ioParam.ioResult);
  218. #define shake ((SerShk *)&((CntrlParam*)pb)->csParam[0])
  219.     shake->errs = FALSE;
  220.     shake->evts = FALSE;
  221.     shake->fDTR = FALSE;
  222.     shake->fInX = FALSE;
  223.     if(XonXoff && (World->machineType >= envMachUnknown)){
  224.         shake->fXOn = TRUE;
  225.         shake->fCTS = FALSE;
  226.         shake->xOn = XONCR;
  227.         shake->xOff = XOFFCR;
  228.     }
  229.     else {
  230.         shake->fXOn = FALSE;
  231.         shake->fCTS = TRUE;
  232.     }
  233.     ((CntrlParam *)pb)->csCode = SERSHAKE;
  234.     PBControl(pb,FALSE);
  235.     if (pb->ioParam.ioResult != noErr) return(pb->ioParam.ioResult);
  236.  
  237.     pb->ioParam.ioPosMode = 0;
  238.     pb->ioParam.ioPosOffset = 0;
  239.     return(noErr);
  240. }
  241. printstring(s,string)   /* Send a printer control string to the */
  242.             /* serial driver. */
  243. register DPstorage  s;
  244. unsigned char   *string;
  245. {
  246.     s->iopb.ioParam.ioBuffer = (Ptr)(string+1);
  247.     s->iopb.ioParam.ioReqCount = (long)(string[0]);
  248.     asyncwrite(s,checkabort);
  249. }
  250. /*
  251.  * dumptop() - Called to dump the top window to the screen.  Nothing if
  252.  * top window is a color window.
  253.  *
  254.  * Uses PtInRgn() to determine the extent of the window's structure region,
  255.  * so we can print the entire window frame.  If you know of a better way,
  256.  * let me know.  Question:  What do we do if we get a round window?
  257.  */
  258. dumptop(s,obuf)
  259. DPstorage s;
  260. short *obuf;
  261. {
  262.     EventRecord myevent;
  263.     GrafPtr tmpport,oldport;
  264.     int i,rows,width,first_row,last_row,left_offset;
  265.     Rect printrect;
  266.     GetPort(&oldport);
  267.     tmpport = (GrafPtr)FrontWindow();
  268.     SetPort(tmpport);
  269. /*
  270.  * Expand portRect to Rect enclosing Window structure region.
  271.  */
  272.         printrect = tmpport->portRect;
  273.         LocalToGlobal(&printrect);
  274.         LocalToGlobal(&printrect.bottom);
  275.         while(PtInRgn(*(Point*)(&printrect),((WindowPeek)tmpport)->strucRgn)){
  276.             printrect.left--;
  277.         }
  278.         printrect.left++;
  279.         while(PtInRgn(*(Point*)(&printrect),((WindowPeek)tmpport)->strucRgn)){
  280.             printrect.top--;
  281.         }
  282.         printrect.top++;
  283.         while(PtInRgn(*(Point*)(&printrect.bottom),((WindowPeek)tmpport)->strucRgn)){
  284.             printrect.bottom++;
  285.         }
  286.         printrect.bottom--;
  287.         while(PtInRgn(*(Point*)(&printrect.bottom),((WindowPeek)tmpport)->strucRgn)){
  288.             printrect.right++;
  289.         }
  290.         printrect.right--;
  291.         GlobalToLocal(&printrect);
  292.         GlobalToLocal(&printrect.bottom);
  293. /*
  294.  * Calculate rows in BitMap which we need to print.  Send sixteen at a
  295.  * time to the BitMap printing routine.
  296.  */
  297.         first_row = printrect.top - tmpport->portBits.bounds.top;
  298.         last_row = printrect.bottom - tmpport->portBits.bounds.top;
  299.         left_offset = printrect.left - tmpport->portBits.bounds.left;
  300.         width = printrect.right - printrect.left;
  301.         for(i=first_row;i<=last_row;i+=16){
  302.             bitmap_to_hires(s,&tmpport->portBits,i,
  303.                 obuf,width,left_offset,last_row-i+1);
  304.         }
  305.     SetPort(oldport);
  306. }
  307. /*
  308.  * dumpscreen() - Called to dump the screen to the printer.  Dumps the
  309.  * WMgrPort instead.  Funky stuff if screen is not in two-color mode.
  310.  */
  311. dumpscreen(s,obuf)
  312. DPstorage s;
  313. short *obuf;
  314. {
  315.     GrafPtr tmpport;
  316.     int i,rows,width;
  317.     GetWMgrPort(&tmpport);
  318.         rows = tmpport->portBits.bounds.bottom - tmpport->portBits.bounds.top;
  319.         width = tmpport->portRect.right - tmpport->portRect.left - 1;
  320.         for(i=0;i<=rows;i+=16){
  321.             bitmap_to_hires(s,&tmpport->portBits,i,obuf,width,0,rows-i);
  322.         }
  323. }
  324. /*
  325.  * bitmap_to_hires()
  326.  * This function translates a QuickDraw BitMap to codes which may
  327.  * be sent to a Tandy DMP-110 dot-matrix printer in high-resolution
  328.  * graphics mode.  Graphics codes for this printer in hi-res mode
  329.  * include all eight bit characters.  There are two characters for each
  330.  * column of 16 dots on the paper.  The top dot (1) corresponds to bit
  331.  * zero of the first byte sent.  The bottom dot (16) corresponds to bit
  332.  * eight of the second byte.  There are 960 columns, or 1920 bytes, of
  333.  * graphics data to be sent for one line of graphics output.
  334.  * Note that the ToolBox bit manipulation routines use lower-to-upper
  335.  * bit order.
  336.  *
  337.  * No smarts in this routine, the whole 1918 bytes are sent for each
  338.  * line.
  339.  */
  340.  
  341. bitmap_to_hires(s,b,row,obuf,width,left_offset,nleft)
  342. DPstorage s;                  /* Global driver storage. */
  343. BitMap *b;                  /* BitMap to print. */
  344. int row;                  /* Starting row on this pass. */
  345. short *obuf;                  /* Serial port output buffer. */
  346. int width;              /* Print this many columns. */
  347. int left_offset;         /* Start this far from left of b->bounds.left. */
  348. int nleft;                  /* Max number of rows to print. */
  349. {
  350.     short *obytes,*inbytes;
  351.     int column,lastcol,nbits,dot;
  352.  
  353.     for(dot=960;dot-- >0;){
  354.         obuf[dot] = 0;
  355.     }
  356.     obytes = obuf;
  357.     lastcol = (b->bounds.right > 959) ? 959 : b->bounds.right;
  358.     lastcol = (lastcol > width) ? width : lastcol;
  359.     inbytes = (short *)(b->baseAddr + b->rowBytes*row);
  360.     nbits = b->rowBytes*8;
  361. /*
  362.  * Transform QuickDraw BitMap to Tandy DMP-110 high resolution graphics
  363.  * using ToolBox bit-manipulation routines.  Sixteen rows of BitMap
  364.  * at column become two-byte printing code.
  365.  */
  366.     for(column=left_offset;column<=lastcol+left_offset;column++){
  367.         for(dot=0;dot<8 && dot<nleft;dot++){
  368.             if(BitTst(inbytes,(long)(column+dot*nbits))){
  369.                 BitSet(obytes,(long)(7-dot));
  370.             }
  371.         }
  372.         for(dot=8;dot<16 && dot < nleft;dot++){
  373.             if(BitTst(inbytes,(long)(column+dot*nbits))){
  374.                 BitSet(obytes,(long)(23-dot));
  375.             }
  376.         }
  377.         obytes++;
  378.     }
  379.     output_hires_data(s,obuf,(long)(lastcol)-1);  /* Send to printer. */
  380. }
  381. /*
  382.  * output_hires_data() - Send a stream of high resolution graphics data
  383.  * to the Tandy DMP-110.  The codes for transferring the graphics, and
  384.  * for the high-resolution paper advance, are hard-coded into this
  385.  * routine.  Skips blank graphics codes, and repositions the printer
  386.  * head.  The overhead is 8 bytes for repositioning the head and restarting
  387.  * high resolution graphics.  If there are four blank columns, then, we
  388.  * break even.  If there are more, we win.  If there are less than four
  389.  * blank columns, we lose.
  390.  */
  391. output_hires_data(s,buf,columns)
  392. DPstorage s;
  393. short *buf;
  394. long columns;
  395. {
  396.  
  397.     short *p;
  398.     short pos,ncodes,start,max;
  399.     max = (columns > 959) ? 959 : columns;
  400.     for(pos=0,ncodes=0,p=buf,start=0;pos<=columns;pos++){
  401.         if(buf[pos] == 0){
  402.             if(ncodes != 0){
  403.                 sendcodes(s,p,ncodes,start);
  404.                 ncodes = 0;
  405.             }
  406.         }else{
  407.             if(ncodes == 0){
  408.                 p = &buf[pos];
  409.                 start = pos;
  410.             }
  411.             ncodes++;
  412.         }
  413.     }
  414.     if(ncodes != 0){
  415.         sendcodes(s,p,ncodes,start);
  416.     }
  417.     printstring(s,prhireslf);
  418. }
  419. sendcodes(s,buf,n,pos)
  420. DPstorage s;
  421. char *buf;
  422. int n,pos;
  423. {
  424.     unsigned char codes[10];
  425.     s->iopb.ioParam.ioBuffer = (Ptr)&codes[0];
  426.     codes[0] = 27;
  427.     codes[1] = 16;
  428.     codes[2] = (pos>>8)&3;
  429.     codes[3] = pos & 0xFF;
  430.     codes[4] = 27;
  431.     codes[5] = 73;
  432.     codes[6] = (n>>8)&3;
  433.     codes[7] = n&0xFF;
  434.     s->iopb.ioParam.ioReqCount = 8;
  435.     asyncwrite(s,checkabort);         /* Send 8 bytes to printer. */
  436.     s->iopb.ioParam.ioBuffer = &buf[0];
  437.     s->iopb.ioParam.ioReqCount = 2*n;
  438.     asyncwrite(s,checkabort);            /* Print the buffer. */
  439. }
  440.  
  441. /*
  442.  * checkabort() - Returns TRUE if command-'.' detected.  Otherwise,
  443.  * FALSE.
  444.  */
  445. checkabort()
  446. {
  447.     EventRecord myevent;
  448.     if(WaitNextEvent(everyEvent,&myevent,0L/*WaitTime*/,0L)){
  449.         if(LoWord(myevent.message & charCodeMask) == '.' &&
  450.             (myevent.modifiers & cmdKey) ){
  451.             PrSetError(iIOAbortErr);
  452.             return TRUE;
  453.             }
  454.     }
  455.     return FALSE;
  456. }
  457. /*
  458.  * Routine to write asynchronously to the serial port, and run an
  459.  * idle procedure while we wait.  The idle routine may call PrSetError(),
  460.  * so we check PrError() for abort each time.  Kill pending serial port
  461.  * IO if abort detected.
  462.  */
  463. asyncwrite(s,idle)
  464. DPstorage s;
  465. ProcPtr idle;
  466. {
  467.     IOParam *p = &(s->iopb.ioParam);
  468.     PBWrite(p,TRUE);                         /* Issue ASYNC write. */
  469.     do{
  470.         (*idle)();                               /* Idle til done. */
  471.         if(PrError() != noErr){                /* Check for abort. */
  472.             if(p->ioResult > 0){                    /* More chars? */
  473.                 PBKillIO(p,FALSE);                 /* Stop output. */
  474.             }
  475.             longjmp(s->abortbuf,1);                    /* Get out. */
  476.         }
  477.     }while(p->ioResult > 0);                /* Check for complete. */
  478. }
  479.